{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import math\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv(\n",
    "        './heart.csv', \n",
    "        usecols=[\n",
    "                'RestingBP',        # resting blood pressure 静息血压\n",
    "                'Cholesterol',      # serum cholesterol 血清胆固醇\n",
    "                'MaxHR',            # maximum heart rate achieved 最大心率\n",
    "                'HeartDisease'\n",
    "        ],\n",
    "        dtype=int,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 删除有0、为空的列\n",
    "df: pd.DataFrame = df[\n",
    "        (df['RestingBP'] != 0) & (df['RestingBP'].notna()) &\n",
    "        (df['Cholesterol'] != 0) & (df['Cholesterol'].notna()) &\n",
    "        (df['MaxHR'] != 0) & (df['MaxHR'].notna())\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['RestingBP_c'] = pd.cut(df['RestingBP'], [80, 100, 120, 140, 160, 180, 200])\n",
    "df['Cholesterol_c'] = pd.cut(df['Cholesterol'], [80, 140, 280, 350, 420, 490, 560, 630])\n",
    "df['MaxHR_c'] = pd.cut(df['MaxHR'], [60, 82, 104, 126, 148, 170, 192, 214])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def func(dfx):\n",
    "    s = pd.Series({ 'True': len(dfx[dfx['HeartDisease'] == 1]),\n",
    "                    'False': len(dfx[dfx['HeartDisease'] == 0])})\n",
    "    s['Total'] = s['True'] + s['False']\n",
    "    s['pyi'] = s['True'] / 356\n",
    "    s['pni'] = s['False'] / 390\n",
    "    s['WOE'] = math.log(s['pyi'] / s['pni'])\n",
    "    s['IV'] = (s['pyi'] - s['pni']) * s['WOE']\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>True</th>\n",
       "      <th>False</th>\n",
       "      <th>Total</th>\n",
       "      <th>pyi</th>\n",
       "      <th>pni</th>\n",
       "      <th>WOE</th>\n",
       "      <th>IV</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RestingBP_c</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>(80, 100]</th>\n",
       "      <td>6.0</td>\n",
       "      <td>10.0</td>\n",
       "      <td>16.0</td>\n",
       "      <td>0.016854</td>\n",
       "      <td>0.025641</td>\n",
       "      <td>-0.419610</td>\n",
       "      <td>0.003687</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(100, 120]</th>\n",
       "      <td>76.0</td>\n",
       "      <td>133.0</td>\n",
       "      <td>209.0</td>\n",
       "      <td>0.213483</td>\n",
       "      <td>0.341026</td>\n",
       "      <td>-0.468400</td>\n",
       "      <td>0.059741</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(120, 140]</th>\n",
       "      <td>160.0</td>\n",
       "      <td>182.0</td>\n",
       "      <td>342.0</td>\n",
       "      <td>0.449438</td>\n",
       "      <td>0.466667</td>\n",
       "      <td>-0.037617</td>\n",
       "      <td>0.000648</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(140, 160]</th>\n",
       "      <td>91.0</td>\n",
       "      <td>54.0</td>\n",
       "      <td>145.0</td>\n",
       "      <td>0.255618</td>\n",
       "      <td>0.138462</td>\n",
       "      <td>0.613091</td>\n",
       "      <td>0.071828</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(160, 180]</th>\n",
       "      <td>19.0</td>\n",
       "      <td>10.0</td>\n",
       "      <td>29.0</td>\n",
       "      <td>0.053371</td>\n",
       "      <td>0.025641</td>\n",
       "      <td>0.733070</td>\n",
       "      <td>0.020328</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(180, 200]</th>\n",
       "      <td>4.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>5.0</td>\n",
       "      <td>0.011236</td>\n",
       "      <td>0.002564</td>\n",
       "      <td>1.477510</td>\n",
       "      <td>0.012813</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              True  False  Total       pyi       pni       WOE        IV\n",
       "RestingBP_c                                                             \n",
       "(80, 100]      6.0   10.0   16.0  0.016854  0.025641 -0.419610  0.003687\n",
       "(100, 120]    76.0  133.0  209.0  0.213483  0.341026 -0.468400  0.059741\n",
       "(120, 140]   160.0  182.0  342.0  0.449438  0.466667 -0.037617  0.000648\n",
       "(140, 160]    91.0   54.0  145.0  0.255618  0.138462  0.613091  0.071828\n",
       "(160, 180]    19.0   10.0   29.0  0.053371  0.025641  0.733070  0.020328\n",
       "(180, 200]     4.0    1.0    5.0  0.011236  0.002564  1.477510  0.012813"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "RestingBP_iv = df.groupby('RestingBP_c').apply(func)\n",
    "RestingBP_iv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\86151\\AppData\\Local\\Temp/ipykernel_31164/421265688.py:7: RuntimeWarning: divide by zero encountered in double_scalars\n",
      "  s['WOE'] = math.log(s['pyi'] / s['pni'])\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>True</th>\n",
       "      <th>False</th>\n",
       "      <th>Total</th>\n",
       "      <th>pyi</th>\n",
       "      <th>pni</th>\n",
       "      <th>WOE</th>\n",
       "      <th>IV</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Cholesterol_c</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>(80, 140]</th>\n",
       "      <td>8.0</td>\n",
       "      <td>6.0</td>\n",
       "      <td>14.0</td>\n",
       "      <td>0.022472</td>\n",
       "      <td>0.015385</td>\n",
       "      <td>0.378898</td>\n",
       "      <td>0.002685</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(140, 280]</th>\n",
       "      <td>248.0</td>\n",
       "      <td>312.0</td>\n",
       "      <td>560.0</td>\n",
       "      <td>0.696629</td>\n",
       "      <td>0.800000</td>\n",
       "      <td>-0.138358</td>\n",
       "      <td>0.014302</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(280, 350]</th>\n",
       "      <td>85.0</td>\n",
       "      <td>60.0</td>\n",
       "      <td>145.0</td>\n",
       "      <td>0.238764</td>\n",
       "      <td>0.153846</td>\n",
       "      <td>0.439523</td>\n",
       "      <td>0.037323</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(350, 420]</th>\n",
       "      <td>10.0</td>\n",
       "      <td>9.0</td>\n",
       "      <td>19.0</td>\n",
       "      <td>0.028090</td>\n",
       "      <td>0.023077</td>\n",
       "      <td>0.196577</td>\n",
       "      <td>0.000985</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(420, 490]</th>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>0.002809</td>\n",
       "      <td>0.005128</td>\n",
       "      <td>-0.601931</td>\n",
       "      <td>0.001396</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(490, 560]</th>\n",
       "      <td>3.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>0.008427</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>inf</td>\n",
       "      <td>inf</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(560, 630]</th>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>0.002809</td>\n",
       "      <td>0.002564</td>\n",
       "      <td>0.091216</td>\n",
       "      <td>0.000022</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                True  False  Total       pyi       pni       WOE        IV\n",
       "Cholesterol_c                                                             \n",
       "(80, 140]        8.0    6.0   14.0  0.022472  0.015385  0.378898  0.002685\n",
       "(140, 280]     248.0  312.0  560.0  0.696629  0.800000 -0.138358  0.014302\n",
       "(280, 350]      85.0   60.0  145.0  0.238764  0.153846  0.439523  0.037323\n",
       "(350, 420]      10.0    9.0   19.0  0.028090  0.023077  0.196577  0.000985\n",
       "(420, 490]       1.0    2.0    3.0  0.002809  0.005128 -0.601931  0.001396\n",
       "(490, 560]       3.0    0.0    3.0  0.008427  0.000000       inf       inf\n",
       "(560, 630]       1.0    1.0    2.0  0.002809  0.002564  0.091216  0.000022"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Cholesterol_c_iv = df.groupby('Cholesterol_c').apply(func)\n",
    "Cholesterol_c_iv # 这里可以看到，如果分组不当，就有可能出现IV值算不了的情况，这也是它的毛病"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>True</th>\n",
       "      <th>False</th>\n",
       "      <th>Total</th>\n",
       "      <th>pyi</th>\n",
       "      <th>pni</th>\n",
       "      <th>WOE</th>\n",
       "      <th>IV</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>MaxHR_c</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>(60, 82]</th>\n",
       "      <td>3.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>5.0</td>\n",
       "      <td>0.008427</td>\n",
       "      <td>0.005128</td>\n",
       "      <td>0.496681</td>\n",
       "      <td>0.001638</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(82, 104]</th>\n",
       "      <td>40.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>55.0</td>\n",
       "      <td>0.112360</td>\n",
       "      <td>0.038462</td>\n",
       "      <td>1.072045</td>\n",
       "      <td>0.079222</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(104, 126]</th>\n",
       "      <td>121.0</td>\n",
       "      <td>49.0</td>\n",
       "      <td>170.0</td>\n",
       "      <td>0.339888</td>\n",
       "      <td>0.125641</td>\n",
       "      <td>0.995186</td>\n",
       "      <td>0.213215</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(126, 148]</th>\n",
       "      <td>107.0</td>\n",
       "      <td>108.0</td>\n",
       "      <td>215.0</td>\n",
       "      <td>0.300562</td>\n",
       "      <td>0.276923</td>\n",
       "      <td>0.081914</td>\n",
       "      <td>0.001936</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(148, 170]</th>\n",
       "      <td>73.0</td>\n",
       "      <td>143.0</td>\n",
       "      <td>216.0</td>\n",
       "      <td>0.205056</td>\n",
       "      <td>0.366667</td>\n",
       "      <td>-0.581169</td>\n",
       "      <td>0.093923</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(170, 192]</th>\n",
       "      <td>11.0</td>\n",
       "      <td>71.0</td>\n",
       "      <td>82.0</td>\n",
       "      <td>0.030899</td>\n",
       "      <td>0.182051</td>\n",
       "      <td>-1.773569</td>\n",
       "      <td>0.268079</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>(192, 214]</th>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>0.002809</td>\n",
       "      <td>0.005128</td>\n",
       "      <td>-0.601931</td>\n",
       "      <td>0.001396</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             True  False  Total       pyi       pni       WOE        IV\n",
       "MaxHR_c                                                                \n",
       "(60, 82]      3.0    2.0    5.0  0.008427  0.005128  0.496681  0.001638\n",
       "(82, 104]    40.0   15.0   55.0  0.112360  0.038462  1.072045  0.079222\n",
       "(104, 126]  121.0   49.0  170.0  0.339888  0.125641  0.995186  0.213215\n",
       "(126, 148]  107.0  108.0  215.0  0.300562  0.276923  0.081914  0.001936\n",
       "(148, 170]   73.0  143.0  216.0  0.205056  0.366667 -0.581169  0.093923\n",
       "(170, 192]   11.0   71.0   82.0  0.030899  0.182051 -1.773569  0.268079\n",
       "(192, 214]    1.0    2.0    3.0  0.002809  0.005128 -0.601931  0.001396"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "MaxHR_iv = df.groupby('MaxHR_c').apply(func)\n",
    "MaxHR_iv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.16904432416698373, 0.05671470075307539, 0.6594102683267432)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "RestingBP_iv['IV'].sum(), Cholesterol_c_iv[Cholesterol_c_iv['IV']!=np.inf]['IV'].sum(), MaxHR_iv['IV'].sum()"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "615239b34ccb10bb668b0a244696df5a416601ba100848cb279f00617abd6cf1"
  },
  "kernelspec": {
   "display_name": "ml_env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
